package wsclient

import (
	"bytes"
	"fmt"
	"github.com/cloudwego/hertz/pkg/common/hlog"
	"github.com/gorilla/websocket"
	jsoniter "github.com/json-iterator/go"
	"reflect"
	"sync"
	"time"
)

const (
	WsUrl = "ws://127.0.0.1:9905/ws"

	PingWait = 30 * time.Second
)

var (
	clientInstance *Client
	once           sync.Once
)

type Client struct {
	Conn *websocket.Conn

	url   string
	token string

	//断线重连通道
	reconnectCh chan bool

	//连上时事件处理
	connectedHandler func()

	//断线时事件处理
	disconnectedHandler func()

	//收到消息事件处理
	//recMsgHandler RecMsgHandler

	handlers map[int]*Handler
}

func (this *Client) WithUrl(url string) *Client {
	this.url = url
	return this
}

func (this *Client) WithToken(token string) *Client {
	this.token = token
	return this
}

func (this *Client) SetConnectedHandler(h func()) *Client {
	this.connectedHandler = h
	return this
}

func (this *Client) SetDisconnectedHandler(h func()) *Client {
	this.disconnectedHandler = h
	return this
}

//func (this *Client) SetRecMsgHandler(h RecMsgHandler) *Client {
//	this.recMsgHandler = h
//	return this
//}

func (this *Client) CheckConn() bool {
	return this.Conn != nil
}

func (this *Client) RegHandler(cmd int, handler *Handler) *Client {
	this.handlers[cmd] = handler
	return this
}

func (this *Client) getHandler(cmd int) (handler *Handler, data any, ok bool) {
	//this.msgHandlersRWMutex.Lock()
	//defer this.msgHandlersRWMutex.Unlock()
	if handler, ok = this.handlers[cmd]; ok {
		if handler.Data != nil {
			data = reflect.New(reflect.TypeOf(handler.Data)).Interface() //reset data
		}
	}
	return
}

//心跳会自动进行，不需要手动发送
//func (this *Client) Hb() {
//	for {
//		hlog.Debug("ws send hb")
//		_ = this.SendMessage("Ping")
//		//_ = this.SendMessage(gbase.Map{
//		//	"cmd":  9999,
//		//	"data": true,
//		//}.ToString())
//		time.Sleep(PingWait)
//	}
//
//}

// connect 建立WebSocket连接
func (this *Client) Connect() {
	for {
		conn, _, err := websocket.DefaultDialer.Dial(fmt.Sprintf("%s?token=%s", this.url, this.token), nil)
		if err != nil {
			hlog.Debugf("Failed to connect to WebSocket server[%s], retrying in 5 seconds...,err:%s", this.url, err.Error())

			//连接失败事件
			if this.disconnectedHandler != nil {
				this.disconnectedHandler()
			}

			time.Sleep(5 * time.Second)
			continue
		}
		this.Conn = conn

		//连接成功事件
		if this.connectedHandler != nil {
			this.connectedHandler()
		}
		hlog.Debugf("Connected to WebSocket server")

		// 启动读取消息的goroutine
		go this.readMessages()

		//go this.Hb()

		// 等待连接断开，然后尝试重连
		<-this.reconnectCh
		if err = this.Conn.Close(); err == nil {
			this.Conn = nil
		}
		hlog.Debugf("Disconnected from WebSocket server, attempting to reconnect...")
		//runtime.EventsEmit("ws:close")
	}
}

// readMessages 读取服务器发送的消息
func (this *Client) readMessages() {
	defer func() {
		this.reconnectCh <- true
	}()
	newline := []byte{'\n'}
	space := []byte{' '}
	for {
		msgType, message, err := this.Conn.ReadMessage()
		if err != nil {
			hlog.Debugf("Error reading message:%s", err.Error())
			//这个情况下会断线网络，比如拔掉网络，会在这里触发
			return // 退出读取循环，触发重连
		}
		message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
		hlog.Debugf("Received msgType:%d message: %s\n", msgType, message)

		switch msgType {
		case websocket.TextMessage:
			msg := &Msg{}
			if !jsoniter.Valid(message) {
				hlog.Errorf("msg is not json fmt")
				//this.SendErr("msg must be json,recv msg:%s", util.Bytes2str(message))
				continue
			} else if err = msg.FromX(msg, message); err != nil {
				hlog.Errorf("construct msg failed,err:%s", err.Error())
				//this.SendErr("construct msg failed,recv msg:%s", util.Bytes2str(message))
				continue
				//} else if err = binding.Validate(msg); err != nil {
				//	hlog.Errorf("msg invalid,err:%s", err.Error())
				//	//this.SendErr("msg invalid,err:%s,recv msg:%s", err.Error(), util.Bytes2str(message))
				//	continue
			}
			//SrvInst().HandleMsg(this, msg)
			//异步处理，避免处理时间过长发生阻塞引起断线
			go this.HandleMsg(msg)

		default:
			hlog.Debugf("wsclient readMessages 未实现该类型的数据处理")
		}

		//if this.recMsgHandler != nil {
		//	this.recMsgHandler(msgType, message)
		//}
	}
}

func (this *Client) HandleMsg(msg *Msg) {
	var (
		handler *Handler
		ok      bool
		err     error
		data    any
	)
	if handler, data, ok = this.getHandler(msg.Cmd); !ok {
		hlog.Debugf("未注册该消息【%d】的处理方法,msg:%s", msg.Cmd, msg.ToString(msg))
		//this.SendErr("未注册该消息【%s】的处理方法", msg.Cmd)
		return
	} else if err = msg.BindVd(data); err != nil {
		hlog.Debugf("绑定数据失败,msg:%s", msg.ToString(msg))
		//c.SendErr(fmt.Sprintf("params err:%s,msg:%s", err.Error(), msg.ToString(msg)))
		return
	}
	if newMsg := handler.Fun(msg, data); newMsg != nil {
		this.Send(newMsg)
	}
}

// SendMessage 发送消息到服务器
func (this *Client) Send(msg *Msg) error {
	hlog.Debugf("wsclient.Send msg:%s", msg.ToString(msg))
	if this.Conn == nil {
		hlog.Debugf("wsclient.Send failed,due to conn closed,msg:%s", msg.ToString(msg))
		return websocket.ErrCloseSent //ErrNotConnected
	}
	return this.Conn.WriteMessage(websocket.TextMessage, msg.ToBytes())
}

func (this *Client) SendErr(fmtStr string, args ...any) {
	msg := NewErrMsg(fmt.Sprintf(fmtStr, args...)) ///.AddSendClient(this)
	this.Send(msg)
}

func ClientInst() *Client {
	once.Do(func() {
		clientInstance = &Client{}
		clientInstance.reconnectCh = make(chan bool)
		clientInstance.handlers = make(map[int]*Handler)
	})
	return clientInstance
}
